﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CrossGenLogic
{
    public class Generator
    {
        private Crossword cross;
        private CSVReader reader;
        private int[] intervals;
        private Random cross_rand;
        private const int wordslimit = 1000;

        public Generator(string pathtovocabulary, string pathtogrid, int diff)
        {
            reader = new CSVReader();
            cross = new Crossword(reader.readgrid(pathtogrid), reader.readvoc(pathtovocabulary, diff),
                reader.Width, reader.Height);
            cross.SortVocabulary();
            cross.SetIntersection();
            SetIntervals();
            cross_rand = new Random(DateTime.Now.Millisecond);
        }

        public Generator(string pathtovocabulary, int WordsCount, int diff)
        {
            reader = new CSVReader();
            GridGeneration GG = new GridGeneration(WordsCount);
            cross = new Crossword(GG.GenerateGrid(), reader.readvoc(pathtovocabulary, diff),
                GG.Width, GG.Height);
            cross.SortVocabulary();
            cross.SetIntersection();
            SetIntervals();
            cross_rand = new Random(DateTime.Now.Millisecond);
        }

        public int CrossHeigth()
        {
            return cross.Height;
        }

        public int CrossWidth()
        {
            return cross.Width;
        }

        private void SetIntervals()
        {
            int i, j;
            intervals = new int[cross.GetWordByNumber(cross.WordsInVoc() - 1).Length + 1];
            for (i = 0; i < intervals.Length; i++)
            {
                for (j = 0; j < cross.WordsInVoc(); j++)
                {
                    if (cross.GetWordByNumber(j).Length == i)
                    {
                        intervals[i] = j;
                        break;
                    }
                }
            }
        }

        public char GetChar(int x, int y)
        {
            return cross.GetCharWithCount(x, y);
        }

        public bool GetCharType(int x, int y)
        {
            return cross.GetCharType(x, y);
        }

        private bool CanPlace(GridElement GE, VocElement VE)
        {
            for (int i = 0; i < VE.Length; i++)
            {
                if (cross.GetCharWithCount(GE.X + i * GE.dx(), GE.Y + i * GE.dy()) != ' ' &&
                    cross.GetCharWithCount(GE.X + i * GE.dx(), GE.Y + i * GE.dy()) != VE.Word[i]) return false;
            }
            return true;
        }

        private void PlaceWord(GridElement GE, VocElement VE)
        {
            for (int i = 0; i < VE.Length; i++)
            {
                cross.SetCharWithCount(GE.X + i * GE.dx(), GE.Y + i * GE.dy(), VE.Word[i]);
            }
            GE.Word = VE;
            GE.IsFill = true;
        }

        private void RemoveWord(GridElement GE)
        {
            if (GE.IsFill)
            {
                for (int i = 0; i < GE.Length; i++)
                {
                    cross.DecreaseCount(GE.X + i * GE.dx(), GE.Y + i * GE.dy());
                }
                GE.IsFill = false;
            }
        }

        public int GetFirstGE()
        {
            return cross.GetFirstGE();
        }

        public bool isGridGenerated()
        {
            if (cross.WordsInCross() == 0) return false;
            else return true;
        }

        private List<int> FindWords(GridElement GE)
        {
            int p;
            int i;
            int count;
            List<int> PossibleWords = new List<int>();
            List<int> RandWords = new List<int>();
            for (p = intervals[GE.Length]; p < intervals[GE.Length + 1]; p++)
            {
                if (!cross.GetWordByNumber(p).Busy && CanPlace(GE, cross.GetWordByNumber(p)))
                {
                    PossibleWords.Add(p);
                }
            }
            if (PossibleWords.Count < wordslimit) count = PossibleWords.Count;
            else count = wordslimit;
            for (p = 0; p < count; p++)
            {
                i = cross_rand.Next(PossibleWords.Count);
                RandWords.Add(PossibleWords[i]);
                PossibleWords.RemoveAt(i);
            }
            return RandWords;
        }

        public void SetIndex()
        {
            int index = 1;
            int temp = 0;
            for (int i = 0; i < cross.Height; i++)
                for (int j = 0; j < cross.Width; j++)
                {
                    for (int k = 0; k < cross.WordsInCross(); k++)
                    {
                        if (cross.GetGEByNumber(k).X == j && cross.GetGEByNumber(k).Y == i)
                        {
                            cross.GetGEByNumber(k).Index = index;
                            temp++;
                        }
                    }
                    if (temp > 0) index++;
                    temp = 0;
                }
        }

        public string GetQuestions()
        {
            SetIndex();
            string AllText = "Список вопросов: " + System.Environment.NewLine;
            string Vertical = System.Environment.NewLine + "По вертикали: ";
            string Horizontal = "По горизонтали: ";
            for (int i = 1; i <= cross.WordsInCross(); i++)
            {
                for (int k = 0; k < cross.WordsInCross(); k++)
                {
                    if (cross.GetGEByNumber(k).Index == i)
                    {
                        if (cross.GetGEByNumber(k).dx() == 1) Horizontal += i.ToString() + ". " + cross.GetGEByNumber(k).Word.Description + " ";
                        else Vertical += i.ToString() + ". " + cross.GetGEByNumber(k).Word.Description + " ";
                    }
                }
            }
            AllText += Horizontal + Vertical;
            return AllText;
        }

        public int WordsCount()
        {
            return cross.WordsInCross();
        }

        public int GetWordIndex(int i)
        {
            return cross.GetGEByNumber(i).Index;
        }

        public GridElement GetGridElement(int i)
        {
            return cross.GetGEByNumber(i);
        }

        public bool Generate(int WordIndex, int PrevIndex)
        {
            List<int> WI = new List<int>();
            WI = FindWords(cross.GetGEByNumber(WordIndex));
            for (int k = 0; k < WI.Count; k++)
            {
                int p = WI[k];
                PlaceWord(cross.GetGEByNumber(WordIndex), cross.GetWordByNumber(p));
                cross.SetBusy(p, true);
                for (int i = 0; i < cross.GetGEByNumber(WordIndex).GetInterCount(); i++)
                {
                    if (!cross.GetGEByNumber(cross.GetGEByNumber(WordIndex).GetIntersection()[i]).IsFill)
                        Generate(cross.GetGEByNumber(WordIndex).GetIntersection()[i], WordIndex);
                }
                if (cross.IsAllInetFill(cross.GetGEByNumber(WordIndex)))
                    return true;
                for (int i = 0; i < cross.GetGEByNumber(WordIndex).GetInterCount(); i++)
                {
                    if (cross.GetGEByNumber(WordIndex).GetIntersection()[i] != PrevIndex)
                        RemoveWord(cross.GetGEByNumber(cross.GetGEByNumber(WordIndex).GetIntersection()[i]));
                }
                RemoveWord(cross.GetGEByNumber(WordIndex));
                cross.SetBusy(p, false);

            }
            return false;
        }

    }
}
